package com.miku.blog.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author shkstart
 * @create ${}YEAR-05-10 22:47
 */
@Component
public class RedisCache {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 设置缓存对象
     * @param key
     * @param value
     */
    public <T> void setCacheObject(String key,T value){
        redisTemplate.opsForValue().set(key,value);
    }


    /**
     * 设置缓存对象
     * @param key
     * @param value
     * @param expireTime   过期时间
     * @param unit  时间单位
     */
    public <T> void setCacheObject(String key,T value,long expireTime,TimeUnit unit){
        redisTemplate.opsForValue().set(key,value,expireTime,unit);
    }

    /**
     * 设置缓存对象过期时间
     * @param key
     * @param expireTime 过期时间
     * @param unit 时间单位
     * @return
     */
    public boolean setCacheObjectExpireTime(String key,long expireTime,TimeUnit unit){
        return redisTemplate.expire(key,expireTime, unit);
    }

    /**
     * 获取key的过期时间 (单位: 秒)
     *
     * 如果该值有过期时间，就返回相应的过期时间;
     * 如果该值没有设置过期时间，就返回-1;
     * 如果没有该值，就返回-2;
     * @param key
     * @return
     */
    public Long getCacheObjectExpireTime(String key){
        return redisTemplate.boundValueOps(key).getExpire();
    }

    /**
     * 删除单个缓存对象
     * @param key
     * @return
     */
    public boolean delCacheObject(String key){
        return redisTemplate.delete(key);
    }

    /**
     * 删除多个缓存对象
     * @param keyList
     * @return
     */
    public Long delCacheObjectList(@NonNull Collection keyList){
        Long count = redisTemplate.delete(keyList);
        return count == null ? 0 : count;
    }

    /**
     * 获取指定的缓存对象
     * @param key
     * @return
     */
    public <T> T getCacheObject(String key){
        ValueOperations<String,T> valueOperations = redisTemplate.opsForValue();
        return valueOperations.get(key);
    }

    /**
     * 获取缓存基本对象列表
     * @return
     */
    public <T> Collection<T> keys(String pattern){
        return redisTemplate.keys(pattern);
    }

    /**
     * 缓存集合数据(从左往右push)  >>>> rightPush
     * @param key
     * @param dataList
     * @param <T>
     * @return
     */
    public <T> Long setCacheList(String key,List<T> dataList){
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获取集合数据
     * @param key
     * @param <T>
     * @return
     */
    public <T> List<T> getCacheList(String key){
        ListOperations<String,T> listOperations = redisTemplate.opsForList();
        return listOperations.range(key,0,-1);
    }


    /**
     * 缓存Set数据
     * @param key
     * @param dataSet
     * @param <T>
     * @return
     */
    public <T> Long setCacheSet(String key,@NonNull Set<T> dataSet){
        Object[] objs = new Object[dataSet.size()];
        int i = 0;
        for (T t : dataSet){
            objs[i++] = t;
        }
        Long count = redisTemplate.opsForSet().add(key, objs);
        return count == null ? 0 : count;
    }

    /**
     * 获取Set数据
     * @param key
     * @param <T>
     * @return
     */
    public <T> Set<T> getCacheSet(String key){
        return redisTemplate.opsForSet().members(key);
    }


    /**
     * 缓存hash数据
     * @param key
     * @param dataMap
     * @param <T>
     * @return
     */
    public <T> void setCacheHash(String key,@NonNull Map<String,T> dataMap){
        redisTemplate.opsForHash().putAll(key, dataMap);
    }

    /**
     * 获取hash数据
     * @param key
     * @param <T>
     * @return
     */
    public <T> Map<String,T> getCacheHash(String key){
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 往hash中添加 键值对
     * @param key
     * @param hashKey
     * @param value
     * @param <T>
     */
    public <T> void setCacheHashMap(String key,String hashKey,T value){
        redisTemplate.opsForHash().put(key,hashKey,value);
    }

    /**
     * 获取hash中对应键的值
     * @param key
     * @param hashKey
     * @param <T>
     * @return
     */
    public <T> T getCacheHashMapValue(String key,String hashKey){
        HashOperations<String,String,T> hashOps = redisTemplate.opsForHash();
        return hashOps.get(key, hashKey);
    }

    /**
     * 获取hash中多个键的值的集合
     * @param key
     * @param hashKeys
     * @param <T>
     * @return
     */
    public <T> List<T> getCacheHashMapValues(String key,@NonNull Collection hashKeys){
        return redisTemplate.opsForHash().multiGet(key, hashKeys);
    }


    /**
     * 删除hash中对应的数据
     * @param key
     * @param objects
     */
    public Long delCacheHashMaps(String key,@NonNull Object... objects){
        return redisTemplate.opsForHash().delete(key, objects);
    }


    /**
     * 设置缓存对象 [该对象在缓存中不存在] [默认过期时间为 5s]
     * @param key 键
     * @param value 数据
     * @param <T> 数据类型
     * @return 若缓存中不存在该对象[key]，则返回true，否则返回false
     */
    public <T> boolean setCacheObjectIfAbsent(String key,T value){
        return redisTemplate.opsForValue().setIfAbsent(key, value,5 * 1000,TimeUnit.MILLISECONDS);
    }

    /**
     * 设置缓存对象 [该对象在缓存中不存在]
     * @param key 键
     * @param value 数据
     * @param timeout 过期时间
     * @param unit 时间单位
     * @param <T> 数据类型
     * @return 若缓存中不存在该对象[key]，则返回true，否则返回false
     */
    public <T> boolean setCacheObjectIfAbsent(String key, T value, Long timeout, TimeUnit unit){
        return redisTemplate.opsForValue().setIfAbsent(key, value,timeout,unit);
    }

    /**
     * 设置缓存对象 [该对象在缓存中存在] [默认过期时间为 5s]
     * @param key 键
     * @param value 数据
     * @param <T> 数据类型
     * @return 若缓存中存在该对象[key]，则返回true，否则返回false
     */
    public <T> boolean setCacheObjectIfPresent(String key,T value){
        return redisTemplate.opsForValue().setIfPresent(key, value,5 * 1000,TimeUnit.MILLISECONDS);
    }

    /**
     * 设置缓存对象 [该对象在缓存中存在]
     * @param key 键
     * @param value 数据
     * @param timeout 过期时间
     * @param unit 时间单位
     * @param <T> 数据类型
     * @return 若缓存中存在该对象[key]，则返回true，否则返回false
     */
    public <T> boolean setCacheObjectIfPresent(String key,T value,Long timeout,TimeUnit unit){
        return redisTemplate.opsForValue().setIfPresent(key, value,timeout,unit);
    }



    /**
     * 设置缓存Hash对象 [该对象在缓存中不存在]
     * @param hashKey 键
     * @param key hash键
     * @param value 数据
     * @param <T> 数据类型
     * @return 若缓存中不存在该对象[key]，则返回true，否则返回false
     */
    public <T> boolean setCacheHashMapIfAbsent(String hashKey,String key,T value){
        BoundHashOperations<String,String,T> boundHashOps = redisTemplate.boundHashOps(hashKey);
        return boundHashOps.putIfAbsent(key, value);
    }


    /**默认增量1
     * 递增 key的值 [值必须是Integer类型]
     * 原子性[单个命令 要么执行 要么不执行]
     * @param key
     * @return
     */
    public void incrementByCacheObject(String key){
        redisTemplate.opsForValue().increment(key);
    }

    /**
     * 递增 key的值 [值必须是Integer类型]
     * 原子性[单个命令 要么执行 要么不执行]
     * @param key
     * @param delta 递增量
     * @return
     */
    public void incrementByCacheObject(String key,Integer delta){
        redisTemplate.opsForValue().increment(key,delta);
    }


    /**默认减量1
     * 递减 key的值 [值必须是Integer类型]
     * 原子性[单个命令 要么执行 要么不执行]
     * @param key
     * @return
     */
    public void decrementByCacheObject(String key){
        redisTemplate.opsForValue().decrement(key);
    }

    /**
     * 递减 key的值 [值必须是Integer类型]
     * 原子性[单个命令 要么执行 要么不执行]
     * @param key
     * @param delta 递减量
     * @return
     */
    public void decrementByCacheObject(String key,Integer delta){
        redisTemplate.opsForValue().decrement(key,delta);
    }

    /**
     * 递增 hash中的对应key的值[值必须是Integer类型]
     * 原子性[单个命令 要么执行 要么不执行]
     * @param key
     * @param hashKey
     * @param delta 递增量
     */
    public void incrementByCacheHash(String key,String hashKey,long delta){
        redisTemplate.opsForHash().increment(key,hashKey,delta);
    }


    /**
     * 是否存在某键
     * @param key
     * @return
     */
    public boolean hasKey(String key){
        return redisTemplate.hasKey(key);
    }


    /**
     * 是否存在某Hash键
     * @param key
     * @return
     */
    public boolean hasHashKey(String hashKey,String key){
        BoundHashOperations boundHashOps = redisTemplate.boundHashOps(hashKey);
        return boundHashOps.hasKey(key);
    }


    /**
     * 获取hash键集合
     * @param hashKey
     * @param <T>
     * @return
     */
    public <T> Set<T> getHashKeys(String hashKey){
        BoundHashOperations boundHashOps = redisTemplate.boundHashOps(hashKey);
        return boundHashOps.keys();
    }


    /**
     * 执行rua脚本
     * @param redisScript
     * @param key
     * @param objs
     * @param <T>
     * @return
     */
    public <T> Object execute(RedisScript redisScript,List<T> key,Object... objs){
        return redisTemplate.execute(redisScript, key, objs);
    }

}
